<?php
namespace pfp\lib {
    
    \defined('_JEXEC') or die('Restricted access');

    /**
     * Database tablename: tbl_users
     * cols:
     *   id               bigint(20)   PrimaryKey
     *   username         varchar(255) Index, Unique
     *   password         varchar(255)
     *   email_address    varchar(255) Index, Unique
     *   date_created     datetime     Default 0000-00-00 00:00:00 
     *   date_last_access datetime     Default 0000-00-00 00:00:00 
     *   status           int(10)      Index, Default 1
     * 
     * Database tablename: tbl_users_data
     * cols:
     *   id               bigint(20)    PrimaryKey
     *   user_id          bigint(20)    Index
     *   user_data_key    varchar(255)  Index
     *   user_data_value  varchar(4096) Default NULL 
     * 
     * Database tablename: tbl_users_groups
     * cols:
     *   id               bigint(20)    PrimaryKey
     *   user_id	      bigint(20)    Index
     *   group_id	      bigint(20)    Index
     *   description      varchar(255)  Default NULL 
     *   date_created     datetime      Default 0000-00-00 00:00:00 
     * 
     * 
     * Database tablename: tbl_users_status
     * cols:
     *   id                        bigint(10)    PrimaryKey
     *   users_status_name         varchar(255)  Index, Default 'active'
     *   users_status_description  varchar(255)  Default NULL 
     * 
     * ---------------------------------------------------------------------------
     */

    /**
     * Description of User
     * 
     * @todo 
     *
     * @author George Zeakis<zeageorge@google.com>
     */
    class User {

        /**
         *
         * @var Session
         */
        protected $_session = \NULL;

        /**
         *
         * @var DatabaseAdapterInterface
         */
        protected $_db = \NULL;

        /**
         *
         * @var array
         */
        protected $_vars = \NULL;

        /**
         *
         * @var array
         */
        protected $_events = array();

        /**
         * @param array $params An assoc array structd:
         *                                    'storage'=>config->pfp->storage,
         *                                    'config' =>config->pfp->users                            
         * @return User Returns User object or throws an codeExecution_FlowControlException(array('return'=>FALSE)) on error
         */
        public function __construct(array $params){
            $tmp = __FUNCTION__."_tmp"; if (isset($this->_vars["$tmp"])){ unset($this->_vars["$tmp"]); }
            try {
                $this->registerEvents();
                $this->_vars    = $params;
                $this->_db      = new DatabaseAdapter();
                $this->_session = new Session($this->_vars['storage']->users_sessions_database);

                $eventData = array('event_name'=>$this->_events['event_onBefore_UserType_Check'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);

                if ($this->isFirstTimeVisitor()){
                    $temp_arr = $this->getUserDataFromDatabase($this->_vars["config"]->rules->guest_userID());

                               // todo: here, check if user is active !!!!

                    foreach ($temp_arr as $key => $value) { $newKey = "user_".$key; $this->_session->{$newKey} = $value; }
                    $this->_session->user_is_guest           = \TRUE;
                    $this->_session->repopulate_user_data    = \FALSE;
                    $this->_session->user_date_last_access   = \date ("Y-m-d H:i:s", \time());
                    $this->_session->user_isFirstTimeVisitor = \FALSE;
                    $this->_session->save();
                    unset($temp_arr);

                    $eventData = array('event_name'=>$this->_events['event_onFirstTime_VisitorRequest'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);
    //                echo "I'm First time visitor !!!!"."<br />\n";
                } elseif ($this->isGuest()){
                    $this->_session->user_date_last_access   = \date ("Y-m-d H:i:s", \time());
                    $this->_session->save();
                    $eventData = array('event_name'=>$this->_events['event_onGuestUserRequest'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);
    //                echo "I'm Guest !!!!"."<br />\n";
                } elseif ($this->isLoggedIn()){
                    // todo: here, check if user is active !!!!
                    $this->_session->user_is_guest           = \FALSE;
                    $this->_session->repopulate_user_data    = \FALSE;
                    $this->_session->user_date_last_access   = \date ("Y-m-d H:i:s", \time());
                    $this->_session->user_isFirstTimeVisitor = \FALSE;
                    $this->_session->save();

                    $eventData = array('event_name'=>$this->_events['event_onLoggedinUserRequest'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);
    //                echo "I'm Logged in !!!!"."<br />\n";
                }
                $eventData = array('event_name'=>$this->_events['event_onAfter_UserType_Check'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);
                if (isset($this->_vars["$tmp"])){ unset($this->_vars["$tmp"]); }
            } catch (codeExecution_FlowControlException $exc) {
                $this->_vars["$tmp"]["errors"] = $exc;
                $eventData = array('event_name'=>$this->_events['event_onError'],'args'=>&$this->_vars); eventManager::riseEvent($eventData);
                unset($this->_vars["$tmp"]); 
                throw new codeExecution_FlowControlException(array('return'=>\FALSE), $exc->getMessage());
            }
        }

        private function registerEvents(){
            $this->_events = array(
                'event_onStart'                            =>'event_onStart',
                'event_onError'                            =>'event_onError',

                'event_onFirstTime_VisitorRequest'         =>'event_onFirstTime_VisitorRequest',
                'event_onGuestUserRequest'                =>'event_onGuestUserRequest',
                'event_onLoggedinUserRequest'             =>'event_onLoggedinUserRequest',

                'event_onBefore_UserType_Check'            =>'event_onBefore_UserType_Check',
                'event_onAfter_UserType_Check'             =>'event_onAfter_UserType_Check',

                'event_onBefore_getUserDataFromDatabase'   =>'event_onBefore_getUserDataFromDatabase',
                'event_onAfter_getUserDataFromDatabase'    =>'event_onAfter_getUserDataFromDatabase',

                'event_onBefore_getUserExtraDataFromDatabase'=>'event_onBefore_getUserExtraDataFromDatabase',
                'event_onAfter_getUserExtraDataFromDatabase' =>'event_onAfter_getUserExtraDataFromDatabase',

                'event_onBefore_getUserGroupsFromDatabase'=>'event_onBefore_getUserGroupsFromDatabase',
                'event_onAfter_getUserGroupsFromDatabase' =>'event_onAfter_getUserGroupsFromDatabase',

                'event_onBefore_getUserGroupsIDsFromDatabase'=>'event_onBefore_getUserGroupsIDsFromDatabase',
                'event_onAfter_getUserGroupsIDsFromDatabase'=>'event_onAfter_getUserGroupsIDsFromDatabase',

                'event_onBefore_getUserMenusFromDatabase' => 'event_onBefore_getUserMenusFromDatabase',
                'event_onAfter_getUserMenusFromDatabase' => 'event_onAfter_getUserMenusFromDatabase',

                'event_onBefore_getUserResourcesFromDatabase' => 'event_onBefore_getUserResourcesFromDatabase',
                'event_onAfter_getUserResourcesFromDatabase' => 'event_onAfter_getUserResourcesFromDatabase',

                'event_onBefore_getUser_ResourcesTypesFromDatabase'=>'event_onBefore_getUser_ResourcesTypesFromDatabase',
                'event_onAfter_getUser_ResourcesTypesFromDatabase'=>'event_onAfter_getUser_ResourcesTypesFromDatabase',

            );
            eventManager::registerEvents($this->_events);
        }

        /**
         * 
         * @return boolean Returns TRUE if user is Guest
         */
        public function isGuest(){
            $tmp = __FUNCTION__."_tmp"; if (isset($this->_vars["$tmp"])){ unset($this->_vars["$tmp"]); }
            if (!isset($this->_session->user_is_guest)) { return \TRUE; }
            return (boolean)$this->_session->user_is_guest;
        }

        /**
         * 
         * @return boolean Returns TRUE if user is First time visitor (Guest)
         */
        public function isFirstTimeVisitor(){
            $tmp = __FUNCTION__."_tmp"; if (isset($this->_vars["$tmp"])){ unset($this->_vars["$tmp"]); }
            if (!isset($this->_session->user_isFirstTimeVisitor)) { return \TRUE; }
            return (boolean)$this->_session->user_isFirstTimeVisitor;
        }

        /**
         * 
         * @return boolean
         */
        public function isLoggedIn(){
            $tmp = __FUNCTION__."_tmp"; if (isset($this->_vars["$tmp"])){ unset($this->_vars["$tmp"]); }

            return (boolean)!$this->isGuest();
        }

        /**
         * 
         * @return array 
         */
        public function get_groups(){ return $this->_session->user_groups; }

        /**
         * 
         * @return array
         */
        public function get_extra_data(){ return $this->_session->user_extra_data; }

        /**
         * 
         * @return string
         */
        public function get_date_last_access(){ return (string)$this->_session->user_date_last_access; }

        /**
         * 
         * @return array
         */
        public function get_resources(){ return $this->_session->user_resources; }

        /**
         * 
         * @return array
         */
        public function get_menus(){ return $this->_session->user_menus; }

        /**
         * 
         * @return string
         */
        public function get_id(){ return (string)$this->_session->user_id; }

        /**
         * 
         * @return string
         */
        public function get_username(){ return (string)$this->_session->user_username; }

        /**
         * 
         * @return string
         */
        public function get_email_address(){ return (string)$this->_session->user_email_address; }

        /**
         * 
         * @return string
         */
        public function get_date_created(){ return (string)$this->_session->user_date_created; }

        /**
         * 
         * @return string
         */
        public function get_status(){ return (string)$this->_session->user_status; }

        /**
         * 
         * @return array
         */
        public function get_all_user_data2array(){ return $this->_session->session_data_toArray(); }

        public function __destruct() { }


        // TODO: implement the followings =========================================

        /**
         * 
         * @param array $user_data
         * @return boolean
         */
        public function login(array $user_data){
            $tmp = __FUNCTION__."_tmp"; if (isset($this->_vars["$tmp"])){ unset($this->_vars["$tmp"]); }
            // todo: here, check if user is active !!!!
            // todo:
            if ($this->_session->user->username==="guest") { 
                // We must NOT allow login of guest account, it is a special account !!!
                // log the error cause something is not right (eg. security)!!!
                return \FALSE;
            }    

            // todo: check if user data exists in session storage, if yes then load them from session storage,
                   // if not, load them from database.
                   // eg. if user requests for the first time right after his login,
                   // then load from database, else load from session storage.
                   // 
                   // todo: check if the ip address, browser, etc. of the client are the same before and after login
                   // 
                   // todo: check if user/session timeout 
                   //
                   //
                   //
                   if (!$this->_session->regenerate_id(\TRUE)){ return \FALSE; }; if (!$this->getSessionDataFromDatabase()) { return \FALSE; }

                   //if user data exists in session storage, load them from there, else: load them from database as follows:
                            if (!$this->getUserDataFromDatabase(\FALSE)){ return \FALSE; }



            // todo: if user successfuly logedin then:
            //=============================================================
            $this->_vars['userData']['session']['session_data'] = array(
                'user_is_guest'=>\FALSE,
                'user_id'=>'', // todo: get user_id and assign it here
                'repopulate'=>\FALSE, // repopulate data from users tables ?
                'username'=>'',
                'email_address'=>'',
                'date_created'=>'',
                'date_last_access'=>'',
                'status'=>'',   //integer: status_id
                'extra_data'=>array(),
                'groups'=>array(),
            );
            if (!$this->_session->save_session($this->_vars['userData']['session']['session_data'])){ return \FALSE; }
            //=============================================================
        }

        /**
         * 
         * @param string|NULL $redirect_to Optional. Default = NULL
         * @return boolean Returns TRUE on success, or throws an codeExecution_FlowControlException(array('return'=>FALSE)) on error
         */
        public function logout($redirect_to=\NULL){
            $tmp = __FUNCTION__."_tmp"; if (isset($this->_vars["$tmp"])){ unset($this->_vars["$tmp"]); }
            try {
                $this->_vars["$tmp"]['user_logout_redirectTo'] = $redirect_to;

                $eventData = array('event_name'=>$this->_events['event_onBefore_User_Logout'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);

                $this->_session->delete_session();
                $this->_session = \NULL;

                if (!\is_null($this->_vars["$tmp"]['user_logout_redirectTo'])){
                    //here use a function to send redirection data (in json format) to client
                    // On client, javascript will handle the redirection.
                }

                $eventData = array('event_name'=>$this->_events['event_onAfter_User_Logout'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);
                if (isset($this->_vars["$tmp"])){ unset($this->_vars["$tmp"]); }
                return \TRUE;
            } catch (codeExecution_FlowControlException $exc) {
                $this->_vars["$tmp"]["errors"] = $exc;
                $eventData = array('event_name'=>$this->_events['event_onError'],'args'=>&$this->_vars); eventManager::riseEvent($eventData);
                unset($this->_vars["$tmp"]); 
                throw new codeExecution_FlowControlException(array('return'=>\FALSE), $exc->getMessage());
            }
        }


        // ========================================================================



        /**
         * @param string $user_id 
         * @return array Returns assoc array on success, or throws an codeExecution_FlowControlException(array('return'=>FALSE)) on error
         */
        protected function getUserDataFromDatabase($user_id){
            $tmp = __FUNCTION__."_tmp"; if (isset($this->_vars["$tmp"])){ unset($this->_vars["$tmp"]); }
            try {
                $this->_vars["$tmp"]['tableName']  = $this->_vars['storage']->users_database->table_prefix().$this->_vars['storage']->users_database->table_users();
                $this->_vars["$tmp"]['sqlQuery']   = "SELECT * FROM `".$this->_vars["$tmp"]['tableName']."` WHERE `id`=:uID LIMIT 1";
                $this->_vars["$tmp"]['bindParams'] = array('uID'=>(string)$user_id);

                $eventData = array('event_name'=>$this->_events['event_onBefore_getUserDataFromDatabase'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);

                $this->_db->connect($this->_vars['storage']->users_database);
                $this->_vars["$tmp"]['getUserDataFromDatabase_selectResults'] = $this->_db->select($this->_vars["$tmp"]['sqlQuery'], $this->_vars["$tmp"]['bindParams']);
                $this->_db->disconnect();

                $this->_vars["$tmp"]["returnResults"] = array();
                if (\count($this->_vars["$tmp"]['getUserDataFromDatabase_selectResults'])==1){
                    foreach ($this->_vars["$tmp"]['getUserDataFromDatabase_selectResults'][0] as $key => $value) {
                                     if ($key=="password") continue; // exclude password field !!!
                        $this->_vars["$tmp"]["returnResults"]["$key"] = (string)$value;
                    }
                    $this->_vars["$tmp"]["returnResults"]['date_last_access'] = \date ("Y-m-d H:i:s", \time());
                    $this->_vars["$tmp"]["returnResults"]['extra_data']       = $this->getUser_ExtraDataFromDatabase($this->_vars["$tmp"]["returnResults"]["id"]);
                    $this->_vars["$tmp"]["returnResults"]['groups']           = $this->getUser_GroupsFromDatabase($this->_vars["$tmp"]["returnResults"]["id"]);
                    $this->_vars["$tmp"]["returnResults"]['resources']        = $this->getUser_ResourcesFromDatabase($this->_vars["$tmp"]["returnResults"]["id"]);
                    $this->_vars["$tmp"]["returnResults"]['menus']            = $this->getUser_MenusFromDatabase($this->_vars["$tmp"]["returnResults"]["id"]);
                }

                $eventData = array('event_name'=>$this->_events['event_onAfter_getUserDataFromDatabase'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);
            } catch (codeExecution_FlowControlException $exc) {
                $this->_vars["$tmp"]["errors"] = $exc;
                $eventData = array('event_name'=>$this->_events['event_onError'],'args'=>&$this->_vars); eventManager::riseEvent($eventData);
                unset($this->_vars["$tmp"]); 
                throw new codeExecution_FlowControlException(array('return'=>\FALSE), $exc->getMessage());
            }
            $re = $this->_vars["$tmp"]["returnResults"]; unset($this->_vars["$tmp"]);
            return $re;
        }

        /**
         * 
         * @param string $userID
         * @return array Returns assoc array or throws an codeExecution_FlowControlException(array('return'=>FALSE)) on error
         */
        protected function getUser_ExtraDataFromDatabase($userID){
            $tmp = __FUNCTION__."_tmp"; if (isset($this->_vars["$tmp"])){ unset($this->_vars["$tmp"]); }
            try {
                $this->_vars["$tmp"]["requested_userID"] = (string)$userID;
                $this->_vars["$tmp"]['tableName']   = $this->_vars['storage']->users_database->table_prefix().$this->_vars["storage"]->users_database->table_users_data();
                $this->_vars["$tmp"]['sqlQuery']    = "SELECT * FROM `".$this->_vars["$tmp"]['tableName']."` WHERE `user_id`=:uID";
                $this->_vars["$tmp"]['bindParams']  = array('uID'=>$this->_vars["$tmp"]["requested_userID"]);

                $eventData = array('event_name'=>$this->_events['event_onBefore_getUserExtraDataFromDatabase'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);

                $this->_db->connect($this->_vars['storage']->users_database);
                $this->_vars["$tmp"]['getUserExtraDataFromDatabase_selectResults'] = $this->_db->select($this->_vars["$tmp"]['sqlQuery'], $this->_vars["$tmp"]['bindParams']);
                $this->_db->disconnect();

                $eventData = array('event_name'=>$this->_events['event_onAfter_getUserExtraDataFromDatabase'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);
            } catch (codeExecution_FlowControlException $exc) {
                $this->_vars["$tmp"]["errors"] = $exc;
                $eventData = array('event_name'=>$this->_events['event_onError'],'args'=>&$this->_vars); eventManager::riseEvent($eventData);
                unset($this->_vars["$tmp"]); 
                throw new codeExecution_FlowControlException(array('return'=>\FALSE), $exc->getMessage());
            }
            $re = $this->_vars["$tmp"]['getUserExtraDataFromDatabase_selectResults']; unset($this->_vars["$tmp"]);
            return $re;
        }

        /**
         * 
         * @param string $userID
         * @return array Returns assoc array or throws an codeExecution_FlowControlException(array('return'=>FALSE)) on error
         */
        protected function getUser_GroupsFromDatabase($userID){
            $tmp = __FUNCTION__."_tmp"; if (isset($this->_vars["$tmp"])){ unset($this->_vars["$tmp"]); }
            try {
                $this->_vars["$tmp"]["requested_userID"]       = (string)$userID;
                $this->_vars["$tmp"]['tableName_users_groups'] = $this->_vars["storage"]->users_database->table_prefix().$this->_vars["storage"]->users_database->table_users_groups();

                $this->_vars["$tmp"]['tableName_groups']       = $this->_vars["storage"]->groups_database->table_prefix().$this->_vars["storage"]->groups_database->table_groups();
                $this->_vars["$tmp"]['tableName_groups_status']= $this->_vars["storage"]->groups_database->table_prefix().$this->_vars["storage"]->groups_database->table_groups_status();

                // if both tables groups are in the same physical server:
                if ($this->_vars["storage"]->users_database->type()==$this->_vars["storage"]->groups_database->type() &&
                    $this->_vars["storage"]->users_database->host()==$this->_vars["storage"]->groups_database->host() &&
                    $this->_vars["storage"]->users_database->port()==$this->_vars["storage"]->groups_database->port() &&
                    $this->_vars["storage"]->users_database->name()==$this->_vars["storage"]->groups_database->name() &&
                    // we also check for usernames too, just in case of different access permissions in the same database....
                    $this->_vars["storage"]->users_database->username()==$this->_vars["storage"]->groups_database->username()){
    //                $sql = "SELECT `tbl_groups`.* FROM `tbl_groups` INNER JOIN "
    //                        . "(SELECT `tbl_users_groups`.`group_id` FROM `tbl_users_groups` WHERE `tbl_users_groups`.`user_id`=:uID) AS `T` "
    //                        . "ON `tbl_groups`.`id`=`T`.`group_id` WHERE `tbl_groups`.`status`=1";
                    $this->_vars["$tmp"]['sqlQuery'] = "SELECT `".$this->_vars["$tmp"]['tableName_groups']."`.* FROM `"
                            .$this->_vars["$tmp"]['tableName_groups']."` INNER JOIN ".
                            "(SELECT `".$this->_vars["$tmp"]['tableName_users_groups']."`.`group_id` FROM `".$this->_vars["$tmp"]['tableName_users_groups']."` ".
                            "WHERE `".$this->_vars["$tmp"]['tableName_users_groups']."`.`user_id`=:uID) AS `T` ".
                            "ON `".$this->_vars["$tmp"]['tableName_groups']."`.`id`=`T`.`group_id` WHERE `".$this->_vars["$tmp"]['tableName_groups']."`.`status`=1";
                    // if a group is different than active (group.status=1 = active_id) then DONOT assign it to user's groups list !!!!!
                    $this->_vars["$tmp"]['bindParams']  = array('uID'=>$this->_vars["$tmp"]["requested_userID"]);

                    $eventData = array('event_name'=>$this->_events['event_onBefore_getUserGroupsFromDatabase'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);

                    $this->_db->connect($this->_vars['storage']->users_database);
                    $this->_vars["$tmp"]['getUserGroupsFromDatabase_selectResults'] = $this->_db->select($this->_vars["$tmp"]['sqlQuery'], $this->_vars["$tmp"]['bindParams']);
                    $this->_db->disconnect();
                } else {// tables groups are in different physical servers:
                    $this->_vars["$tmp"]["getUser_groups_IDs_fromDatabase_Results"] = $this->getUser_groups_IDs_fromDatabase($this->_vars["$tmp"]["requested_userID"]);
                    $this->_vars["$tmp"]["tmp_groups_ids"] = array();
                    foreach ($this->_vars["$tmp"]['getUser_groups_IDs_fromDatabase_Results'] as $key => $value) { $this->_vars["$tmp"]["tmp_groups_ids"][] = (int)$value["group_id"]; }
                    unset($this->_vars["$tmp"]['getUser_groups_IDs_fromDatabase_Results']);

                    if (\count($this->_vars["$tmp"]["tmp_groups_ids"])>0){ // we have groups ids....continue to get other data based on ids, from other database
                        $this->_vars["$tmp"]['sqlQuery'] = "SELECT `".$this->_vars["$tmp"]['tableName_groups']."`.* FROM `".$this->_vars["$tmp"]['tableName_groups']."` ".
                            "WHERE `".$this->_vars["$tmp"]['tableName_groups']."`.`id` IN (".  \implode(",", $this->_vars["$tmp"]["tmp_groups_ids"]).")";

                        $eventData = array('event_name'=>$this->_events['event_onBefore_getUserGroupsFromDatabase'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);

                        $this->_db->connect($this->_vars['storage']->groups_database);
                        $this->_vars["$tmp"]['getUserGroupsFromDatabase_selectResults'] = $this->_db->select($this->_vars["$tmp"]['sqlQuery']);
                        $this->_db->disconnect();
                    } else {
                        $eventData = array('event_name'=>$this->_events['event_onBefore_getUserGroupsFromDatabase'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);
                        $this->_vars["$tmp"]['getUserGroupsFromDatabase_selectResults'] = $this->_vars["$tmp"]["tmp_groups_ids"];
                        unset($this->_vars["$tmp"]["tmp_groups_ids"]);
                    }
                }
                $eventData = array('event_name'=>$this->_events['event_onAfter_getUserGroupsFromDatabase'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);
            } catch (codeExecution_FlowControlException $exc) {
                $this->_vars["$tmp"]["errors"] = $exc;
                $eventData = array('event_name'=>$this->_events['event_onError'],'args'=>&$this->_vars); eventManager::riseEvent($eventData);
                unset($this->_vars["$tmp"]); 
                throw new codeExecution_FlowControlException(array('return'=>\FALSE), $exc->getMessage());
            }
            $re = $this->_vars["$tmp"]['getUserGroupsFromDatabase_selectResults']; unset($this->_vars["$tmp"]);
            return $re;
        }

        /**
         * 
         * @param string $userID
         * @return array Returns assoc array or throws an codeExecution_FlowControlException(array('return'=>FALSE)) on error
         */
        protected function getUser_MenusFromDatabase($userID){
            $tmp = __FUNCTION__."_tmp"; if (isset($this->_vars["$tmp"])){ unset($this->_vars["$tmp"]); }
            try {
                $this->_vars["$tmp"]["requested_userID"] = (string)$userID;
                $this->_vars["$tmp"]['tableName_users_groups'] = $this->_vars["storage"]->users_database->table_prefix().$this->_vars["storage"]->users_database->table_users_groups();

                $this->_vars["$tmp"]['tableName_menus']        = $this->_vars["storage"]->users_menus_database->table_prefix().$this->_vars["storage"]->users_menus_database->table_menus();
                $this->_vars["$tmp"]['tableName_menus_groups'] = $this->_vars["storage"]->users_menus_database->table_prefix().$this->_vars["storage"]->users_menus_database->table_menus_groups();
                $this->_vars["$tmp"]['tableName_menus_status'] = $this->_vars["storage"]->users_menus_database->table_prefix().$this->_vars["storage"]->users_menus_database->table_menus_status();
                $this->_vars["$tmp"]['tableName_menus_types']  = $this->_vars["storage"]->users_menus_database->table_prefix().$this->_vars["storage"]->users_menus_database->table_menus_types();

                // if both tables groups are in the same physical server:
                if ($this->_vars["storage"]->users_database->type()==$this->_vars["storage"]->users_menus_database->type() &&
                    $this->_vars["storage"]->users_database->host()==$this->_vars["storage"]->users_menus_database->host() &&
                    $this->_vars["storage"]->users_database->port()==$this->_vars["storage"]->users_menus_database->port() &&
                    $this->_vars["storage"]->users_database->name()==$this->_vars["storage"]->users_menus_database->name() &&
                    // we also check for usernames too, just in case of different access permissions in the same database....
                    $this->_vars["storage"]->users_database->username()==$this->_vars["storage"]->users_menus_database->username()){
                    // should we get DISTINCT rows ? or NOT ?
        //            $sql = "SELECT `tbl_menus`.* FROM `tbl_menus` INNER JOIN (`tbl_menus_groups` "
        //                    . " INNER JOIN (SELECT `tbl_users_groups`.`group_id` FROM `tbl_users_groups` WHERE `tbl_users_groups`.`user_id`=:uID) AS `T` "
        //                    . "ON `tbl_menus_groups`.`group_id`=`T`.`group_id`) "
        //                    . "ON `tbl_menus`.`id`=`tbl_menus_groups`.`menu_id` WHERE `tbl_menus`.`status`=1";
                    $this->_vars["$tmp"]['sqlQuery'] = "SELECT DISTINCT `".$this->_vars["$tmp"]['tableName_menus']."`.* FROM `".$this->_vars["$tmp"]['tableName_menus'].
                            "` INNER JOIN (`".$this->_vars["$tmp"]['tableName_menus_groups']."` ".
                            "INNER JOIN (SELECT `".$this->_vars["$tmp"]['tableName_users_groups'].
                            "`.`group_id` FROM `".$this->_vars["$tmp"]['tableName_users_groups'].
                            "` WHERE `".$this->_vars["$tmp"]['tableName_users_groups']."`.`user_id`=:uID) AS `T` ".
                            "ON `".$this->_vars["$tmp"]['tableName_menus_groups']."`.`group_id`=`T`.`group_id`) ".
                            "ON `".$this->_vars["$tmp"]['tableName_menus']."`.`id`=`".$this->_vars["$tmp"]['tableName_menus_groups']."`.`menu_id` WHERE `".
                            $this->_vars["$tmp"]['tableName_menus']."`.`status`=1";
                    // if a menu is different than active (menu.status=1 = active_id) then DONOT assign it to user's menus list !!!!!
                    $this->_vars["$tmp"]['bindParams']  = array('uID'=>$this->_vars["$tmp"]["requested_userID"]);

                    $eventData = array('event_name'=>$this->_events['event_onBefore_getUserMenusFromDatabase'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);

                    $this->_db->connect($this->_vars['storage']->users_database);
                    $this->_vars["$tmp"]['getUserMenusFromDatabase_selectResults'] = $this->_db->select($this->_vars["$tmp"]['sqlQuery'], $this->_vars["$tmp"]['bindParams']);
                    $this->_db->disconnect();
                } else {
                    $this->_vars["$tmp"]["getUser_groups_IDs_fromDatabase_Results"] = $this->getUser_groups_IDs_fromDatabase($this->_vars["$tmp"]["requested_userID"]);
                    $this->_vars["$tmp"]["tmp_groups_ids"] = array();
                    foreach ($this->_vars["$tmp"]['getUser_groups_IDs_fromDatabase_Results'] as $key => $value) { $this->_vars["$tmp"]["tmp_groups_ids"][] = (int)$value["group_id"]; }
                    unset($this->_vars["$tmp"]['getUser_groups_IDs_fromDatabase_Results']);

                    if (\count($this->_vars["$tmp"]["tmp_groups_ids"])>0){ // we have groups ids....continue to get other data based on ids, from other database
                           // should we get DISTINCT rows ? or NOT ?
    //                    SELECT `tbl_menus`.* FROM `tbl_menus` INNER JOIN 
    //                         (SELECT `tbl_menus_groups`.`menu_id` FROM `tbl_menus_groups` WHERE `tbl_menus_groups`.`group_id` IN (1)) AS `T` 
    //                    ON `tbl_menus`.`id`=`T`.`menu_id` WHERE `tbl_menus`.`status`=1
                        $this->_vars["$tmp"]['sqlQuery'] = "SELECT DISTINCT `".$this->_vars["$tmp"]['tableName_menus']."`.* FROM `".
                                 $this->_vars["$tmp"]['tableName_menus']."` INNER JOIN ".
                                 "(SELECT `".$this->_vars["$tmp"]['tableName_menus_groups']."`.`menu_id` FROM `".
                                 $this->_vars["$tmp"]['tableName_menus_groups']."` WHERE `".$this->_vars["$tmp"]['tableName_menus_groups'].
                                 "`.`group_id` IN (".  \implode(",", $this->_vars["$tmp"]["tmp_groups_ids"]).")) AS `T` ".
                                 "ON `".$this->_vars["$tmp"]['tableName_menus']."`.`id`=`T`.`menu_id` WHERE `".$this->_vars["$tmp"]['tableName_menus']."`.`status`=1";
                        $eventData = array('event_name'=>$this->_events['event_onBefore_getUserMenusFromDatabase'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);

                        $this->_db->connect($this->_vars['storage']->users_menus_database);
                        $this->_vars["$tmp"]['getUserMenusFromDatabase_selectResults'] = $this->_db->select($this->_vars["$tmp"]['sqlQuery']);
                        $this->_db->disconnect();
                    } else {
                        $eventData = array('event_name'=>$this->_events['event_onBefore_getUserMenusFromDatabase'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);
                        $this->_vars["$tmp"]['getUserMenusFromDatabase_selectResults'] = $this->_vars["$tmp"]["tmp_groups_ids"];
                        unset($this->_vars["$tmp"]["tmp_groups_ids"]);
                    }    
                }
                $eventData = array('event_name'=>$this->_events['event_onAfter_getUserMenusFromDatabase'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);
            } catch (codeExecution_FlowControlException $exc) {
                $this->_vars["$tmp"]["errors"] = $exc;
                $eventData = array('event_name'=>$this->_events['event_onError'],'args'=>&$this->_vars); eventManager::riseEvent($eventData);
                unset($this->_vars["$tmp"]); 
                throw new codeExecution_FlowControlException(array('return'=>\FALSE), $exc->getMessage());
            }
            $re = $this->_vars["$tmp"]['getUserMenusFromDatabase_selectResults']; unset($this->_vars["$tmp"]);
            return $re;
        }

        /**
         * 
         * @param string $userID
         * @return array Returns assoc array or throws an codeExecution_FlowControlException(array('return'=>FALSE)) on error
         */
        protected function getUser_ResourcesFromDatabase($userID){
            $tmp = __FUNCTION__."_tmp"; if (isset($this->_vars["$tmp"])){ unset($this->_vars["$tmp"]); }
            try {
                $this->_vars["$tmp"]["requested_userID"]       = (string)$userID;
                $this->_vars["$tmp"]['tableName_users_groups'] = $this->_vars["storage"]->users_database->table_prefix().$this->_vars["storage"]->users_database->table_users_groups();

                $this->_vars["$tmp"]['tableName_resources']        = $this->_vars["storage"]->users_resources_database->table_prefix().$this->_vars["storage"]->users_resources_database->table_resources();
                $this->_vars["$tmp"]['tableName_resources_groups'] = $this->_vars["storage"]->users_resources_database->table_prefix().$this->_vars["storage"]->users_resources_database->table_resources_groups();
                $this->_vars["$tmp"]['tableName_resources_status'] = $this->_vars["storage"]->users_resources_database->table_prefix().$this->_vars["storage"]->users_resources_database->table_resources_status();
                $this->_vars["$tmp"]['tableName_resources_types']  = $this->_vars["storage"]->users_resources_database->table_prefix().$this->_vars["storage"]->users_resources_database->table_resources_types();

                // if both tables groups are in the same physical server:
                if ($this->_vars["storage"]->users_database->type()==$this->_vars["storage"]->users_resources_database->type() &&
                    $this->_vars["storage"]->users_database->host()==$this->_vars["storage"]->users_resources_database->host() &&
                    $this->_vars["storage"]->users_database->port()==$this->_vars["storage"]->users_resources_database->port() &&
                    $this->_vars["storage"]->users_database->name()==$this->_vars["storage"]->users_resources_database->name() &&
                    // we also check for usernames too, just in case of different access permissions in the same database....
                    $this->_vars["storage"]->users_database->username()==$this->_vars["storage"]->users_resources_database->username()){
    //                $sql = "SELECT DISTINCT `tbl_resources`.* FROM `tbl_resources` INNER JOIN (`tbl_resources_groups` ".
    //                       "INNER JOIN (SELECT `tbl_users_groups`.`group_id` FROM `tbl_users_groups` WHERE `tbl_users_groups`.`user_id`=2) AS `T` ".
    //                       "ON `tbl_resources_groups`.`group_id`=`T`.`group_id`) ".
    //                       "ON `tbl_resources`.`id`=`tbl_resources_groups`.`resource_id` WHERE `tbl_resources`.`status`=1";
                    $this->_vars["$tmp"]['sqlQuery'] = "SELECT DISTINCT `".$this->_vars["$tmp"]['tableName_resources'].
                            "`.* FROM `".$this->_vars["$tmp"]['tableName_resources'].
                            "` INNER JOIN (`".$this->_vars["$tmp"]['tableName_resources_groups']."` ".
                            "INNER JOIN (SELECT `".$this->_vars["$tmp"]['tableName_users_groups'].
                            "`.`group_id` FROM `".$this->_vars["$tmp"]['tableName_users_groups'].
                            "` WHERE `".$this->_vars["$tmp"]['tableName_users_groups']."`.`user_id`=:uID) AS `T` ".
                            "ON `".$this->_vars["$tmp"]['tableName_resources_groups'].
                            "`.`group_id`=`T`.`group_id`) ".
                            "ON `".$this->_vars["$tmp"]['tableName_resources'].
                            "`.`id`=`".$this->_vars["$tmp"]['tableName_resources_groups'].
                            "`.`resource_id` WHERE `".$this->_vars["$tmp"]['tableName_resources']."`.`status`=1";
                    // if a resource is different than active (resource.status=1 = active_id) then DONOT assign it to user's resources list !!!!!
                    $this->_vars["$tmp"]['bindParams']  = array('uID'=>$this->_vars["$tmp"]["requested_userID"]);

                    $eventData = array('event_name'=>$this->_events['event_onBefore_getUserResourcesFromDatabase'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);

                    $this->_db->connect($this->_vars['storage']->users_database);
                    $this->_vars["$tmp"]['getUserResourcesFromDatabase_selectResults'] = $this->_db->select($this->_vars["$tmp"]['sqlQuery'], $this->_vars["$tmp"]['bindParams']);
                    $this->_db->disconnect();
                } else {
                    $this->_vars["$tmp"]["getUser_groups_IDs_fromDatabase_Results"] = $this->getUser_groups_IDs_fromDatabase($this->_vars["$tmp"]["requested_userID"]);
                    $this->_vars["$tmp"]["tmp_groups_ids"] = array();
                    foreach ($this->_vars["$tmp"]['getUser_groups_IDs_fromDatabase_Results'] as $key => $value) { $this->_vars["$tmp"]["tmp_groups_ids"][] = (int)$value["group_id"]; }
                    unset($this->_vars["$tmp"]['getUser_groups_IDs_fromDatabase_Results']);

                    if (\count($this->_vars["$tmp"]["tmp_groups_ids"])>0){ // we have groups ids....continue to get other data based on ids, from other database
    //                    $sql = "SELECT DISTINCT `tbl_resources`.* FROM `tbl_resources` INNER JOIN ".
    //                        "(SELECT `tbl_resources_groups`.`resource_id` FROM `tbl_resources_groups` WHERE `tbl_resources_groups`.`group_id` IN (1,2,3)) AS `T` ".
    //                        "ON `tbl_resources`.`id`=`T`.`resource_id` WHERE `tbl_resources`.`status`=1";
                        $this->_vars["$tmp"]['sqlQuery'] = "SELECT DISTINCT `".$this->_vars["$tmp"]['tableName_resources'].
                                "`.* FROM `".$this->_vars["$tmp"]['tableName_resources']."` INNER JOIN ".
                                "(SELECT `".$this->_vars["$tmp"]['tableName_resources_groups'].
                                "`.`resource_id` FROM `".$this->_vars["$tmp"]['tableName_resources_groups'].
                                "` WHERE `".$this->_vars["$tmp"]['tableName_resources_groups'].
                                "`.`group_id` IN (".  \implode(",", $this->_vars["$tmp"]["tmp_groups_ids"]).")) AS `T` ".
                                "ON `".$this->_vars["$tmp"]['tableName_resources'].
                                "`.`id`=`T`.`resource_id` WHERE `".$this->_vars["$tmp"]['tableName_resources']."`.`status`=1";
                        // we need: WHERE status=1, because we want the active resources.
                        $eventData = array('event_name'=>$this->_events['event_onBefore_getUserResourcesFromDatabase'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);

                        $this->_db->connect($this->_vars['storage']->users_resources_database);
                        $this->_vars["$tmp"]['getUserResourcesFromDatabase_selectResults'] = $this->_db->select($this->_vars["$tmp"]['sqlQuery']);
                        $this->_db->disconnect();
                    } else {
                        $eventData = array('event_name'=>$this->_events['event_onBefore_getUserResourcesFromDatabase'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);
                        $this->_vars["$tmp"]['getUserResourcesFromDatabase_selectResults'] = $this->_vars["$tmp"]["tmp_groups_ids"];
                        unset($this->_vars["$tmp"]["tmp_groups_ids"]);
                    }
                }

                $this->_vars["$tmp"]["userResourcesTypes"] = $this->getUser_ResourcesTypesFromDatabase();
                $ta = $this->_vars["$tmp"]['getUserResourcesFromDatabase_selectResults'];
                $pa = array();

                foreach ($ta as $key => $value) {
                    foreach ($this->_vars["$tmp"]["userResourcesTypes"] as $key1 => $value1) {
                        if ($value["resource_type"]==$value1["id"]){ $value["resource_type"] = $value1["rsrcs_type_name"]; break; }
                    }
                    $rt = $value["resource_type"]; unset($value["status"], $value["resource_type"]);
                    $value["resource"] = (array)\json_decode($value["resource"], \TRUE, 512);
                    $pa[$rt][] = $value;
                }
                $this->_vars["$tmp"]['getUserResourcesFromDatabase_selectResults'] = $pa;

                $eventData = array('event_name'=>$this->_events['event_onAfter_getUserResourcesFromDatabase'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);
            } catch (codeExecution_FlowControlException $exc) {
                $this->_vars["$tmp"]["errors"] = $exc;
                $eventData = array('event_name'=>$this->_events['event_onError'],'args'=>&$this->_vars); eventManager::riseEvent($eventData);
                unset($this->_vars["$tmp"]); 
                throw new codeExecution_FlowControlException(array('return'=>\FALSE), $exc->getMessage());
            }
            $re = $this->_vars["$tmp"]['getUserResourcesFromDatabase_selectResults']; unset($this->_vars["$tmp"]);
            return $re;
        }

        /**
         * 
         * @param string $user_id
         * @return array Returns an assoc array of user's groups id or throws an codeExecution_FlowControlException(array('return'=>FALSE)) on error
         */
        protected function getUser_groups_IDs_fromDatabase($user_id){
            $tmp = __FUNCTION__."_tmp"; if (isset($this->_vars["$tmp"])){ unset($this->_vars["$tmp"]); }
            try {
                $this->_vars["$tmp"]["requested_userID"] = (string)$user_id;
                $this->_vars["$tmp"]['tableName']   = $this->_vars['storage']->users_database->table_prefix().$this->_vars["storage"]->users_database->table_users_groups();
                $this->_vars["$tmp"]['sqlQuery']    = "SELECT `".$this->_vars["$tmp"]['tableName']."`.`group_id` FROM `".$this->_vars["$tmp"]['tableName'].
                                                      "` WHERE `".$this->_vars["$tmp"]['tableName']."`.`user_id`=:uID";
                $this->_vars["$tmp"]['bindParams']  = array('uID'=>$this->_vars["$tmp"]["requested_userID"]);

                $eventData = array('event_name'=>$this->_events['event_onBefore_getUserGroupsIDsFromDatabase'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);

                $this->_db->connect($this->_vars['storage']->users_database);
                $this->_vars["$tmp"]['getUser_groups_IDs_fromDatabase_selectResults'] = $this->_db->select($this->_vars["$tmp"]['sqlQuery'], $this->_vars["$tmp"]['bindParams']);
                $this->_db->disconnect();

                $eventData = array('event_name'=>$this->_events['event_onAfter_getUserGroupsIDsFromDatabase'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);
            } catch (codeExecution_FlowControlException $exc) {
                $this->_vars["$tmp"]["errors"] = $exc;
                $eventData = array('event_name'=>$this->_events['event_onError'],'args'=>&$this->_vars); eventManager::riseEvent($eventData);
                unset($this->_vars["$tmp"]); 
                throw new codeExecution_FlowControlException(array('return'=>\FALSE), $exc->getMessage());
            }
            $re = $this->_vars["$tmp"]['getUser_groups_IDs_fromDatabase_selectResults']; unset($this->_vars["$tmp"]);
            return $re;
        }

        /**
         * @return array Returns an assoc array of user's ResourcesTypes or throws an codeExecution_FlowControlException(array('return'=>FALSE)) on error
         */
        protected function getUser_ResourcesTypesFromDatabase(){
            $tmp = __FUNCTION__."_tmp"; if (isset($this->_vars["$tmp"])){ unset($this->_vars["$tmp"]); }
            try {
                $this->_vars["$tmp"]['tableName']   = $this->_vars['storage']->users_resources_database->table_prefix().$this->_vars["storage"]->users_resources_database->table_resources_types();
                $this->_vars["$tmp"]['sqlQuery']    = "SELECT `".$this->_vars["$tmp"]['tableName']."`.* FROM `".$this->_vars["$tmp"]['tableName'];

                $eventData = array('event_name'=>$this->_events['event_onBefore_getUser_ResourcesTypesFromDatabase'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);

                $this->_db->connect($this->_vars['storage']->users_resources_database);
                $this->_vars["$tmp"]['getUser_ResourcesTypesFromDatabase_selectResults'] = $this->_db->select($this->_vars["$tmp"]['sqlQuery']);
                $this->_db->disconnect();

                $eventData = array('event_name'=>$this->_events['event_onAfter_getUser_ResourcesTypesFromDatabase'], 'args'=>&$this->_vars); eventManager::riseEvent($eventData);
            } catch (codeExecution_FlowControlException $exc) {
                $this->_vars["$tmp"]["errors"] = $exc;
                $eventData = array('event_name'=>$this->_events['event_onError'],'args'=>&$this->_vars); eventManager::riseEvent($eventData);
                unset($this->_vars["$tmp"]); 
                throw new codeExecution_FlowControlException(array('return'=>\FALSE), $exc->getMessage());
            }
            $re = $this->_vars["$tmp"]['getUser_ResourcesTypesFromDatabase_selectResults']; unset($this->_vars["$tmp"]);
            return $re;
        }

    //    protected function turnResources2aTree(array $resources, $rootNodeID="0"){
    //        $tree_array2return = array();
    //        $references = array();
    //        foreach ($resources as $id => &$node) {
    //            $references[$node['id']] = &$node;
    //            $node['c'] = array();
    //
    //            if ($node['parent_id']==$rootNodeID) {
    //                $tree_array2return[$node['id']] = &$node;
    //            } else {
    //                $references[$node['parent_id']]['c'][$node['id']] = &$node;
    //            }
    //        }
    //	return $tree_array2return;
    //    }
    }
}
?>
